/////////////////////////////////////////////////////////////
// Copyright (C) 2006 Dean Lee.
// All rights reserved.
// 
// Author:  Dean Lee
//          mailto:deanlee2@hotmail.com
//          http://www.deanlee.cn
//
// Purpose: Defines the URL Rewriting for wordpress
// ==========================================================
// Revision History:
// 9/2/2006 4:45:51 PM   Dean Lee   Initial Version
// 11/1/2006 1:20:01 PM   Dean Lee   
//		exclude wp-admin and wp-content from url rewriting
// ==========================================================
// Acknowledgements:
//   <> Many thanks to all of you, who have encouraged me to update my articles
//     and code, and who sent in bug reports and fixes.
/////////////////////////////////////////////////////////////

#include "stdafx.h"

#include <httpfilt.h> 
#include <shlwapi.h>
#include <atlstr.h>
#pragma comment(lib, "shlwapi.lib")

#define DEFAULT_BUFFER_SIZE 2048
char *g_pszPath = NULL;
DWORD g_dwAppendBufferLen = 0;

bool DoInitialize(HMODULE hModule);
DWORD DoUrlRewriting(HTTP_FILTER_CONTEXT *pfc, HTTP_FILTER_AUTH_COMPLETE_INFO *pPPH);

#ifdef _MANAGED
#pragma managed(push, off)
#endif

void DlcReportEventA(WORD wType, LPCSTR pszMessage)
{
    HANDLE  hEventSource;
    LPCTSTR  lpszStrings[1];

    lpszStrings[0] = (LPCTSTR)pszMessage;

    /* Get a handle to use with ReportEvent(). */
    hEventSource = RegisterEventSourceA(NULL, "wp-url-rewriting");
    if (hEventSource != NULL)
    {
        /* Write to event log. */
        ReportEventA(hEventSource, wType, 0, 0, NULL, 1, 0, (LPCSTR*) &lpszStrings[0], NULL);
        DeregisterEventSource(hEventSource);
    }
}
#ifdef _DEBUG
#define DLC_TRACE(x) DlcReportEventA(EVENTLOG_INFORMATION_TYPE, x)
#else
#define DLC_TRACE(x) __noop
#endif


BOOL WINAPI __stdcall GetFilterVersion(HTTP_FILTER_VERSION *pVer)
{
    /* Specify the types and order of notification */

    pVer->dwFlags = (SF_NOTIFY_ORDER_DEFAULT | SF_NOTIFY_NONSECURE_PORT | 
        SF_NOTIFY_SECURE_PORT | SF_NOTIFY_PREPROC_HEADERS);

    pVer->dwFilterVersion = MAKELONG( 0, 5 );
    strcpy(pVer->lpszFilterDesc, "wp-url-rewriting, Version 1.1");
    DlcReportEventA(EVENTLOG_INFORMATION_TYPE, "wp-url-rewriting stareted");
    return TRUE;
}

DWORD DoUrlRewriting(HTTP_FILTER_CONTEXT *pfc, HTTP_FILTER_PREPROC_HEADERS *pPPH)
{
    DWORD dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
    if (pfc == NULL || !pPPH)
    {
        return dwRet;
    }
	char szBuffer[DEFAULT_BUFFER_SIZE];
    DWORD cbBuf = DEFAULT_BUFFER_SIZE;
    BOOL bRet = pPPH->GetHeader(pfc, "url", szBuffer, &cbBuf );
	if (bRet && (--cbBuf > g_dwAppendBufferLen) && memcmp(szBuffer, g_pszPath, g_dwAppendBufferLen) == 0)
	{
		char * pStart = szBuffer + g_dwAppendBufferLen;
		const int nUrlLen = cbBuf - g_dwAppendBufferLen;
		if (memchr(pStart, '.', nUrlLen) == NULL)
		{
#ifdef _DEBUG
			char szTrace[1024];
			_snprintf_s(szTrace, _countof(szTrace), _TRUNCATE, "DoUrlRewriting:(%d)%s", nUrlLen, pStart);
			DLC_TRACE(szTrace);
#endif
			if (!(nUrlLen >= 8 && memcmp(pStart, "wp-admin", 8) == 0) 
				&& !(nUrlLen >= 10 && memcmp(pStart, "wp-content", 10) == 0))
			{
				char szNewUrl[DEFAULT_BUFFER_SIZE];
				strncpy_s(szNewUrl, _countof(szNewUrl), g_pszPath, _TRUNCATE);
				strncat_s(szNewUrl, _countof(szNewUrl), "index.php/", _TRUNCATE);
				strncat_s(szNewUrl, _countof(szNewUrl), pStart, _TRUNCATE);
#ifdef _DEBUG
				char szTrace[1024];
				_snprintf_s(szTrace, _countof(szTrace), _TRUNCATE, "new url:%s", szNewUrl);
				DLC_TRACE(szTrace);
#endif
				pPPH->SetHeader(pfc, "url", szNewUrl);
			}
		}
	}
    return dwRet;
}

DWORD DoProcessHeaders(HTTP_FILTER_CONTEXT *pfc, HTTP_FILTER_PREPROC_HEADERS *pFH)
{
	return DoUrlRewriting(pfc, pFH);
}

DWORD WINAPI __stdcall HttpFilterProc(HTTP_FILTER_CONTEXT *pfc, DWORD NotificationType, 
                                      VOID *pvData)
{
    DWORD   status = SF_STATUS_REQ_NEXT_NOTIFICATION; 
    switch (NotificationType)
    {

    case SF_NOTIFY_PREPROC_HEADERS:
        status = DoProcessHeaders(pfc, (HTTP_FILTER_PREPROC_HEADERS*)pvData);
        break;
    }
    return status;
}

bool DoInitialize(HMODULE hModule)
{
    char szFileName[MAX_PATH];
	bool bSuccess = false;
    if (0 != ::GetModuleFileName(hModule, szFileName, MAX_PATH))
    {
        ::PathRemoveFileSpec(szFileName);
        ::PathAppend(szFileName, "config.ini");
        HANDLE hFile = ::CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, 
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile != INVALID_HANDLE_VALUE)
        {
            DWORD dwFileSize = ::GetFileSize(hFile, NULL);
            if (dwFileSize > 0)
            {
                g_pszPath = new char[dwFileSize + 2];
                if (::ReadFile(hFile, g_pszPath, dwFileSize, &g_dwAppendBufferLen, NULL))
                {
                    g_pszPath[g_dwAppendBufferLen] = '\0';
                    StrTrim(g_pszPath, " \r\n\t");
					g_dwAppendBufferLen = strlen(g_pszPath);
					if (g_dwAppendBufferLen > 0)
					{
						// convert '\' to '/';
						char *pszWalker = g_pszPath;
						while (*pszWalker)
						{
							if (*pszWalker == '\\')
								*pszWalker = '/';
							++pszWalker;
						}
						if (g_pszPath[g_dwAppendBufferLen - 1] != '/')
						{
							g_pszPath[g_dwAppendBufferLen] = '/';
							g_pszPath[g_dwAppendBufferLen + 1] = '\0';
							++g_dwAppendBufferLen;
						}
						strlwr(g_pszPath);
						bSuccess = true;
					}
				}
            }
            ::CloseHandle(hFile);
        }
	}
	if (!bSuccess)
	{
		if (!g_pszPath)
			g_pszPath = new char[2];
		g_pszPath[0] = '/';
		g_pszPath[1] = '\0';
		g_dwAppendBufferLen = 1;
	}
#ifdef _DEBUG
		char szTrace[1024];
		_snprintf_s(szTrace, _countof(szTrace), _TRUNCATE, "DoInitialize:(%d)%s", g_dwAppendBufferLen, g_pszPath);
		DLC_TRACE(szTrace);
#endif
    return true;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                      DWORD  ul_reason_for_call,
                      LPVOID lpReserved
                      )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        {
            if (!DoInitialize(hModule))
            {
                return FALSE;
            }
            break;
        }
    case DLL_PROCESS_DETACH:
        {
            if (g_pszPath)
                delete [] g_pszPath;
            g_pszPath = NULL;
            g_dwAppendBufferLen = 0;
        }
        break;
    }
    return TRUE;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif